package com.spider.spiderrule.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.spider.spiderrule.enums.RuleDataStatusEnum;
import com.spider.spiderrule.enums.RuleHeaderTypeEnum;
import com.spider.spiderrule.exception.BusinessException;
import com.spider.spiderrule.mapper.SpiderRuleMapper;
import com.spider.spiderrule.mapstruct.SpiderRuleHeaderMapstruct;
import com.spider.spiderrule.mapstruct.SpiderRuleItemMapstruct;
import com.spider.spiderrule.model.entity.*;
import com.spider.spiderrule.model.vo.RulePublishHeaderVo;
import com.spider.spiderrule.model.vo.RulePublishItemVo;
import com.spider.spiderrule.model.vo.RuleRedisConditionVo;
import com.spider.spiderrule.service.*;
import com.spider.spiderrule.utils.RedisUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;

import java.text.SimpleDateFormat;
import java.util.*;
import java.util.stream.Collectors;


/**
 * @author xiong.bo
 * @version 1.0
 * @date 2024/4/9 11:28 下午
 */

@Service
public class SpiderRuleServiceImpl extends ServiceImpl<SpiderRuleMapper, SpiderRule>
        implements SpiderRuleService {

    @Autowired
    private SpiderRuleDeployService ruleDeployService;
    @Autowired
    private SpiderRuleEditHeaderService ruleEditHeaderService;
    @Autowired
    private SpiderRuleEditItemService ruleEditItemService;
    @Autowired
    private SpiderRulePublishHeaderService rulePublishHeaderService;
    @Autowired
    private SpiderRulePublishItemService rulePublishItemService;
    @Autowired
    private SpiderRuleHisPublishHeaderService ruleHisPublishHeaderService;
    @Autowired
    private SpiderRuleHisPublishItemService ruleHisPublishItemService;

    @Autowired
    private SpiderRuleHeaderMapstruct ruleHeaderMapstruct;
    @Autowired
    private SpiderRuleItemMapstruct ruleItemMapstruct;

    @Autowired
    private RedisUtils redisUtils;

    private Object ruleRedisLockObj = new Object();

    @Override
    public IPage<SpiderRule> selectPage(int page, int pageSize) {
        Page<SpiderRule> pageObject = new Page(page, pageSize);
        LambdaQueryWrapper<SpiderRule> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.orderByDesc(SpiderRule::getCreateTime);
        return this.baseMapper.selectPage(pageObject, queryWrapper);
    }

    @Override
    public SpiderRule findById(Long id) {
        SpiderRule rule = baseMapper.selectById(id);
        if (rule == null) {
            throw new BusinessException(String.format("该规则不存在,id:【%s】", id));
        }
        return rule;
    }

    @Override
    public SpiderRule findByRuleCode(String ruleCode) {
        LambdaQueryWrapper<SpiderRule> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(SpiderRule::getRuleCode, ruleCode);
        List<SpiderRule> ruleList = baseMapper.selectList(queryWrapper);
        if (CollectionUtils.isEmpty(ruleList)) {
            throw new BusinessException(String.format("该规则不存在,ruleCode:【%s】", ruleCode));
        }
        if (ruleList.size() > 1) {
            throw new BusinessException(String.format("该规则编码存在多个,请检查数据,ruleCode:【%s】", ruleCode));
        }
        return ruleList.get(0);
    }

    @Override
    public SpiderRule saveRule(SpiderRule spiderRule) {
        long timeMillis = System.currentTimeMillis();
        SimpleDateFormat sdf = new SimpleDateFormat("yyyyMMddHHmmssSSS");
        String dataStr = sdf.format(timeMillis);
        spiderRule.setRuleCode("DMN-"+dataStr);
        this.baseMapper.insert(spiderRule);
        return spiderRule;
    }

    @Override
    public SpiderRule editRule(SpiderRule spiderRule) {
        SpiderRule rule = this.findById(spiderRule.getId());
        rule.setRuleName(spiderRule.getRuleName());
        rule.setDescription(spiderRule.getDescription());
        baseMapper.updateById(rule);
        return rule;
    }

    @Override
    public Boolean deleteRule(Long id) {
        SpiderRule rule = this.findById(id);
        if (1 == rule.getDataStatus()) {
            throw new BusinessException("发布状态的规则不允许删除,请先作废规则");
        }
        baseMapper.deleteById(id);
        return true;
    }

    @Override
    public Boolean invalidRule(Long id) {
        SpiderRule rule = this.findById(id);
        rule.setDataStatus(RuleDataStatusEnum.INVALID.getCode());
        baseMapper.updateById(rule);
        return true;
    }

    @Override
    public Boolean unInvalidRule(Long id) {
        SpiderRule rule = this.findById(id);
        rule.setDataStatus(RuleDataStatusEnum.SAVE.getCode());
        if(rule.getDeployId() > 0){
            rule.setDataStatus(RuleDataStatusEnum.PUBLISH.getCode());
        }
        baseMapper.updateById(rule);
        return true;
    }

    @Transactional(rollbackFor = Exception.class)
    @Override
    public Boolean releaseRule(Long id) {
        // 1，主表更新
        SpiderRule rule = this.findById(id);

        int latestVersion = ruleDeployService.selectLatestVersion(id);
        SpiderRuleDeploy spiderRuleDeploy = new SpiderRuleDeploy();
        spiderRuleDeploy.setRuleId(id);
        latestVersion++;
        spiderRuleDeploy.setVersion(latestVersion);
        ruleDeployService.save(spiderRuleDeploy);

        rule.setDataStatus(RuleDataStatusEnum.PUBLISH.getCode());
        rule.setDeployId(spiderRuleDeploy.getId());
        rule.setVersion(latestVersion);
        this.baseMapper.updateById(rule);

        // 2，记录规则矩阵发布表
        List<SpiderRulePublishHeader> newPublishHeaderList = new ArrayList<>();
        List<SpiderRulePublishItem> newPublishItemList = new ArrayList<>();
        batchSaveRulePublish(rule, newPublishHeaderList, newPublishItemList);

        // 3，记录规则矩阵历史表
        batchSaveRuleHisPublish(id);

        // 4，封装Redis数据结构，并缓存
        rulePublishToRedis(rule, newPublishHeaderList, newPublishItemList);

        return true;
    }

    @Override
    public RuleRedisConditionVo findRulePublishFromRedis(String ruleCode, String type, String tenantId) {

        Object redisObject;
        String redisKey;
        if (RuleHeaderTypeEnum.CONDITION.getName().equals(type)) {
            redisKey = "spider-rule-condition:" + tenantId + ":ruleCode:" + ruleCode;
        } else if (RuleHeaderTypeEnum.RESULT.getName().equals(type)) {
            redisKey = "spider-rule-result:" + tenantId + ":ruleCode:" + ruleCode;
        } else {
            throw new BusinessException("type参数不正确");
        }
        redisObject = redisUtils.get(redisKey);

        if (redisObject == null) {
            synchronized (ruleRedisLockObj) {
                redisObject = redisUtils.get(redisKey);
                if (redisObject == null) {
                    // 重新加载数据到Redis中
                    rulePublishToRedis(ruleCode);
                    redisObject = redisUtils.get(redisKey);
                    if (redisObject == null) {
                        redisUtils.set(redisKey, "", 60000);
                    }
                }
            }
        }

        RuleRedisConditionVo ruleRedisConditionVo = null;
        if (redisObject instanceof RuleRedisConditionVo) {
            ruleRedisConditionVo = (RuleRedisConditionVo) redisObject;
        }
        return ruleRedisConditionVo;
    }


    /**
     * 记录规则矩阵发布表
     *
     * @param rule                 规则主表
     * @param newPublishHeaderList 发布的规则矩阵表头集合
     * @param newPublishItemList   发布的规则矩阵表行集合
     */
    private void batchSaveRulePublish(SpiderRule rule, List<SpiderRulePublishHeader> newPublishHeaderList,
                                      List<SpiderRulePublishItem> newPublishItemList) {

        // 查询规则矩阵编辑表
        Long ruleId = rule.getId();
        LambdaQueryWrapper<SpiderRuleEditHeader> editHeaderQueryWrapper = new LambdaQueryWrapper<>();
        editHeaderQueryWrapper.eq(SpiderRuleEditHeader::getRuleId, ruleId);
        List<SpiderRuleEditHeader> editHeaderList = ruleEditHeaderService.list(editHeaderQueryWrapper);

        LambdaQueryWrapper<SpiderRuleEditItem> editItemQueryWrapper = new LambdaQueryWrapper<>();
        editItemQueryWrapper.eq(SpiderRuleEditItem::getRuleId, ruleId);
        List<SpiderRuleEditItem> editItemList = ruleEditItemService.list(editItemQueryWrapper);

        for (SpiderRuleEditHeader editHeader : editHeaderList) {
            SpiderRulePublishHeader publishHeader = ruleHeaderMapstruct.toPublishHeader(editHeader);
            publishHeader.setVersion(rule.getVersion());
            publishHeader.setDataStatus(RuleDataStatusEnum.PUBLISH.getCode());
            newPublishHeaderList.add(publishHeader);
        }

        for (SpiderRuleEditItem editItem : editItemList) {
            SpiderRulePublishItem rulePublishItem = ruleItemMapstruct.toPublishItem(editItem);
            rulePublishItem.setVersion(rule.getVersion());
            rulePublishItem.setDataStatus(RuleDataStatusEnum.PUBLISH.getCode());
            newPublishItemList.add(rulePublishItem);
        }

        // 删除发布表的记录
        rulePublishHeaderService.deleteByRuleId(ruleId);
        rulePublishItemService.deleteByRuleId(ruleId);

        //保存到规则矩阵发布表
        rulePublishHeaderService.saveBatch(newPublishHeaderList, newPublishHeaderList.size());
        rulePublishItemService.saveBatch(newPublishItemList, newPublishItemList.size());
    }

    /**
     * 记录规则矩阵历史表
     *
     * @param ruleId 规则ID
     */
    private void batchSaveRuleHisPublish(Long ruleId) {

        // 查询规则矩阵发布表
        LambdaQueryWrapper<SpiderRulePublishHeader> publishHeaderQueryWrapper = new LambdaQueryWrapper<>();
        publishHeaderQueryWrapper.eq(SpiderRulePublishHeader::getRuleId, ruleId);
        List<SpiderRulePublishHeader> dataPublishHeaderList = rulePublishHeaderService.list(publishHeaderQueryWrapper);

        LambdaQueryWrapper<SpiderRulePublishItem> publishItemQueryWrapper = new LambdaQueryWrapper<>();
        publishItemQueryWrapper.eq(SpiderRulePublishItem::getRuleId, ruleId);
        List<SpiderRulePublishItem> dataPublishItemList = rulePublishItemService.list(publishItemQueryWrapper);

        List<SpiderRuleHisPublishHeader> newHisPublishHeaderList = new ArrayList<>();
        List<SpiderRuleHisPublishItem> newHisPublishItemList = new ArrayList<>();
        dataPublishHeaderList.forEach(e -> {
            SpiderRuleHisPublishHeader hisPublishHeader = ruleHeaderMapstruct.toHisPublishHeader(e);
            newHisPublishHeaderList.add(hisPublishHeader);
        });
        dataPublishItemList.forEach(e -> {
            SpiderRuleHisPublishItem hisPublishItem = ruleItemMapstruct.toHisPublishItem(e);
            newHisPublishItemList.add(hisPublishItem);
        });

        // 记录规则矩阵历史表
        ruleHisPublishHeaderService.saveBatch(newHisPublishHeaderList, newHisPublishHeaderList.size());
        ruleHisPublishItemService.saveBatch(newHisPublishItemList, newHisPublishItemList.size());
    }

    /**
     * 将规则发布数据缓存到Redis中
     *
     * @param rule                 规则主表
     * @param newPublishHeaderList 发布的规则矩阵表头集合
     * @param newPublishItemList   发布的规则矩阵表行集合
     */
    private void rulePublishToRedis(SpiderRule rule, List<SpiderRulePublishHeader> newPublishHeaderList,
                                    List<SpiderRulePublishItem> newPublishItemList) {

        List<SpiderRulePublishItem> sortPublishItemList = newPublishItemList.stream()
                .sorted(Comparator.comparing(SpiderRulePublishItem::getRowIndex))
                .collect(Collectors.toList());

        List<RulePublishHeaderVo> conditionFields = new ArrayList<>();// 输入条件表头
        List<RulePublishHeaderVo> resultFields = new ArrayList<>(); // 输出结果表头
        newPublishHeaderList.forEach(e -> {
            RulePublishHeaderVo conditionVo = new RulePublishHeaderVo();
            conditionVo.setHeaderUuid(e.getHeaderUuid());
            conditionVo.setFieldCode(e.getFieldCode());
            conditionVo.setFieldName(e.getFieldName());
            conditionVo.setHeaderType(e.getHeaderType());
            if (e.getHeaderType() == 1) {
                conditionFields.add(conditionVo);
            } else if (e.getHeaderType() == 2) {
                resultFields.add(conditionVo);
            }
        });

        Map<String, List<RulePublishItemVo>> conditionVoListMap = new HashMap<>();// 输入条件表行
        for (RulePublishHeaderVo conditionVo : conditionFields) {
            conditionVoListMap.put(conditionVo.getHeaderUuid(), new ArrayList<>());
        }
        Map<String, List<RulePublishItemVo>> resultVoListMap = new HashMap<>();// 输出结果表行
        for (RulePublishHeaderVo conditionVo : resultFields) {
            resultVoListMap.put(conditionVo.getHeaderUuid(), new ArrayList<>());
        }

        for (SpiderRulePublishItem item : sortPublishItemList) {
            List<RulePublishItemVo> conditionList = conditionVoListMap.get(item.getHeaderUuid());
            if (conditionList != null) {
                RulePublishItemVo resultVo = ruleItemMapstruct.toRulePublishItemVo(item);
                conditionList.add(resultVo);
            }
            List<RulePublishItemVo> resultList = resultVoListMap.get(item.getHeaderUuid());
            if (resultList != null) {
                RulePublishItemVo resultVo = ruleItemMapstruct.toRulePublishItemVo(item);
                resultList.add(resultVo);
            }
        }

        // 输入条件对象
        RuleRedisConditionVo redisConditionVo = new RuleRedisConditionVo();
        redisConditionVo.setConditionVoList(conditionFields);
        redisConditionVo.setConditionVoListMap(conditionVoListMap);

        // 输出结果对象
        RuleRedisConditionVo redisResultVo = new RuleRedisConditionVo();
        redisResultVo.setConditionVoList(resultFields);
        redisResultVo.setConditionVoListMap(resultVoListMap);

        String conditionKey = "spider-rule-condition:" + rule.getTenantId() + ":ruleCode:" + rule.getRuleCode();
        redisUtils.set(conditionKey, redisConditionVo, 3600000);

        String resultKey = "spider-rule-result:" + rule.getTenantId() + ":ruleCode:" + rule.getRuleCode();
        redisUtils.set(resultKey, redisResultVo, 3600000);

    }

    /**
     * 重新加载规则数据到Redis中
     */
    private void rulePublishToRedis(String ruleCode) {
        SpiderRule rule = this.findByRuleCode(ruleCode);
        Long ruleId = rule.getId();
        // 查询规则矩阵发布表
        LambdaQueryWrapper<SpiderRulePublishHeader> publishHeaderQueryWrapper = new LambdaQueryWrapper<>();
        publishHeaderQueryWrapper.eq(SpiderRulePublishHeader::getRuleId, ruleId);
        List<SpiderRulePublishHeader> dataPublishHeaderList = rulePublishHeaderService.list(publishHeaderQueryWrapper);

        LambdaQueryWrapper<SpiderRulePublishItem> publishItemQueryWrapper = new LambdaQueryWrapper<>();
        publishItemQueryWrapper.eq(SpiderRulePublishItem::getRuleId, ruleId);
        List<SpiderRulePublishItem> dataPublishItemList = rulePublishItemService.list(publishItemQueryWrapper);

        this.rulePublishToRedis(rule, dataPublishHeaderList, dataPublishItemList);

    }
}
